#!/bin/sh
#
# This software comes with no warranty or restrictions.
# If you can figure out how to make money from this software then can buy me a beer.
# If you can figure out how to make A LOT of money from this software then you can give me a job.
#
# Lucas Hazel <lucas@die.net.au>
#
. defaults/ppkgmk.conf
#. ppkgmk.conf

message() {
	echo "===> $@"
}

message_n() {
	echo -n "===> $@"
}

do_exit() {
	exit $1
}

warning() {
	message "WARNING: $@"
}

alert() {
	message "ALERT: $@"
}

error() {
	message "ERROR: $@"
	cleanup
	do_exit 1
}

load_file() {
	if [ -f $1 ]; then
		. $1
		return 0
	fi
	return 1
}

version() {
	echo "ppkgmk 0.1 by Lucas Hazel"
	do_exit 0
}

display_help() {
	cat <<EOF
usage: ppkgmk [options]
options:
  -cf, --config-file <file>	use and alternative configuration file
  -d,  --download		download missing source files
  -do, --download-only		terminate once source is downloaded, implies -d
  -uo, --unpack-only 		terminate once source is unpacked, implies -kw
  -uc, --update-checksum    	update checksums
  -ic, --ignore-checksum	ignore checksum mismatches
  -uf, --update-footprint	update footprint
  -if, --ignore-footprint	ignore footprint mismatches
  -c,  --clean			remove temporary files
  -dc, --dist-clean		remove temporary files, retrieved files and pakage
  -kw, --keep-work		keep temporary files
  -v,  --version		display the version
  -h,  --help			dusplay this message
EOF
	do_exit 0
}

parse_options() {
	while [ "$1" ]
	do
		case $1 in
			-cf | --config-file)
				if [ "$2" ]; then
					load_file $2
					if [ $? -eq 1 ]; then
						error "File $2 does not exist or could not be read."
					fi
					shift
				else
					error "The option --config-file required an argument."
				fi
				;;
			-d|--download)
				PPKGMK_DOWNLOAD="true"
				;;
			-do|--download-only)
				PPKGMK_DOWNLOAD="true"
				PPKGMK_DOWNLOAD_ONLY="true"
				;;
			-uo|--unpack-only)
				PPKGMK_UNPACK_ONLY="true"
				;;
			-uc|--update-checksum)
				PPKGMK_UPDATE_CHECKSUM="true"
				;;
			-ic|--ignore-checksum)
				PPKGMK_IGNORE_CHECKSUM="true"
				;;
			-uf|--update-footprint)
				PPKGMK_UPDATE_FOOTPRINT="true"
				;;
			-if|--ignore-footprint)
				PPKGMK_IGNORE_FOOTPRINT="true"
				;;
			-c|--clean)
				PPKGMK_CLEAN_ONLY="true"
				;;
			-dc|--dist-clean)
				PPKGMK_CLEAN_ONLY="true"
				PPKGMK_DIST_CLEAN="true"
				;;
			-kw|--keep-work)
				PPKGMK_KEEP_WORK="true"
				;;
			-v|--version)
				display_version
				;;
			-h|--help)
				display_help
				;;
			*)
				error "$1 is an invalid option"
				;;
		esac
		shift
	done
}

get_local_filename() {
	FILE="`echo $1 | cut -d: -f2-`"
	FILE="`basename \"$FILE\"`"
	FILE="$PPKGMK_SOURCE_DIR/$FILE"
}

get_source_list() {
	for GETTER in $PPKGMK_LOADED_GETTERS
	do
		eval SOURCES=\$${GETTER}_source
		for SOURCE in $SOURCES
		do
			get_local_filename $SOURCE
			LOCAL_SOURCES="$LOCAL_SOURCES $FILE"
		done
	done
}

dist_clean() {
	message "Performing a dist clean."
	rm -rf $PPKGMK_PACKAGE_DIR/$PACKAGE.pkg.tar.gz
	get_source_list
	for SOURCE in $LOCAL_SOURCES
	do
		for PORT_FILE in $port_source
		do
			get_local_filename $PORT_FILE
			if [ "$FILE" = "$SOURCE" ]; then
				DO_NOT_DELETE="true"
			fi
		done
		if [ "$DO_NOT_DELETE" != "true" ]; then
			rm -rf $SOURCE
		fi
		DO_NOT_DELETE="false"
	done
}

cleanup() {
	if [ "$PPKGMK_KEEP_WORK" ]; then
		message "Keeping work."
		return 0
	fi
	message "Cleaning up."
	rm -rf $TMP $SRC $PKG $PPKGMK_WORK_DIR
	if [ "$PPKGMK_DIST_CLEAN" ]; then
		dist_clean
	fi
	if [ "$PPKGMK_CLEAN_ONLY" ]; then
		do_exit 0
	fi
}

load_datafiles() {
	message_n "Loading dataset:"
	for DATA in $PPKGMK_DATASET
	do
		load_file $DATA
		if [ $? -ne 0 ]; then
			echo ''
			warning "File $DATA does not exist or could not be read."
			message_n "..."
		else
			echo -n " $DATA"
			PPKGMK_LOADED_DATA="$PPKGMK_LOADED_DATA $DATA"
		fi
	done
	echo ""
}

create_build_environment() {
	TMP="$PPKGMK_TMP_ROOT/$name"
	SRC="$PPKGMK_WORK_DIR/src"
	PKG="$PPKGMK_WORK_DIR/pkg"
	PACKAGE="$name#$version-$release"
	cleanup
	message "Creating build environment."
	mkdir -p $SRC $PKG $TMP
}

check_var() {
	eval V=\$$1
	if [ "$V" ]; then
		return 0
	fi
	return 1
}

check_required_vars() {
	message_n "Checking for required variables:"
	for VAR in $@
	do
		check_var $VAR
		if [ $? -ne 0 ]; then
			echo ""
			error "Required variable \"$VAR\" was not set."
		fi
		echo -n " $VAR"
	done
	echo ""
}

register_getters() {
	message_n "Registering getter modules:"
	for GETTER in $PPKGMK_GETTERS
	do
		load_file $PPKGMK_GETTER_PATH/$GETTER
		if [ $? -ne 0 ]; then
			echo ""
			warning "Getter module \"$GETTER\" was not found or failed to load."
			message_n "..."
		else
			echo -n " $GETTER"
			PPKGMK_LOADED_GETTERS="$PPKGMK_LOADED_GETTERS $GETTER"
		fi
	done
	echo ""
}

register_unpackers() {
	message_n "Registering unpacker modules:"
	for UNPACKER in $PPKGMK_UNPACKERS
	do
		load_file $PPKGMK_UNPACKER_PATH/$UNPACKER
		if [ $? -ne 0 ]; then
			echo ""
			warning "Unpacker module \"$UNPACKER\" was not found or failed to load."
			message_n "..."
		else
			echo -n " $UNPACKER"
			PPKGMK_LOADED_UNPACKERS="$PPKGMK_LOADED_UNPACKERS $UNPACKER"
		fi
	done
	echo ""
}

load_data() {
	parse_options $@
	register_getters
	register_unpackers
	load_datafiles
	check_required_vars $PPKGMK_REQUIRED_VARS
	create_build_environment
}

get_source() {
	for GETTER in $PPKGMK_LOADED_GETTERS
	do
		GETTER_FUNC=${GETTER}_get
		eval SOURCES=\$${GETTER}_source
		for SOURCE in $SOURCES
		do
			get_local_filename $SOURCE
			if [ ! -e $FILE ]; then
				if [ ! "$PPKGMK_DOWNLOAD" ]; then
					error "Download option not set, use -d"
				fi
				$GETTER_FUNC $SOURCE $FILE
				if [ $? -ne 0 ]; then
					error "Failed to download $SOURCE"
				fi
			fi
		done
	done
	if [ "$PPKGMK_DOWNLOAD_ONLY" ]; then
		do_exit 0
	fi
}

generate_diff() {
	diff -U 0 -w -t $1 $2 | sed '/^@/d' | sed '/^+++/d' | sed '/^---/d' \
		| sed 's/^+/  NEW: /g' \
		| sed 's/^-/  OLD: /g' \
		> $3
}

verify_source() {
	CHECKSUM_FILE=$PPKGMK_ROOT/$PPKGMK_CHECKSUM_FILE
	CHECKSUM_NEW=$TMP/checksum.new
	CHECKSUM_DIFF=$TMP/checksum.diff
	if [ "$PPKGMK_UPDATE_CHECKSUM" ]; then
		message "Updating checksum."
		rm -rf $CHECKFILE
	fi
	if [ ! -f $CHECKSUM_FILE ]; then
		message "Creating checksum."
		PPKGMK_UPDATE_CHECKSUM="true"
	fi
	for GETTER in $PPKGMK_LOADED_GETTERS
	do
		eval SOURCES=\$${GETTER}_source
		CHECKSUM_FUNC=${GETTER}_checksum
		for SOURCE in $SOURCES
		do
			get_local_filename $SOURCE
			CHECKSUM="`$CHECKSUM_FUNC $FILE`"
			if [ "$PPKGMK_UPDATE_CHECKSUM" ]; then
				echo $CHECKSUM >> $CHECKSUM_FILE
			else
				echo $CHECKSUM >> $CHECKSUM_NEW
			fi
		done
	done
	if [ ! "$PPKGMK_UPDATE_CHECKSUM" ]; then
		if [ "$PPKGMK_IGNORE_CHECKSUM" ]; then
			warning "Ignoring checksums."
		else
			message "Verifying source."
			generate_diff $CHECKSUM_FILE $CHECKSUM_NEW $CHECKSUM_DIFF
			if [ -s $CHECKSUM_DIFF ]; then
				alert "Checksum mismatch:"
				cat $CHECKSUM_DIFF
			fi
		fi
	fi
}

unpack_source() {
	get_source_list
	for SOURCE in $LOCAL_SOURCES
	do
		for UNPACKER in $PPKGMK_LOADED_UNPACKERS
		do
			MATCH_FUNC=${UNPACKER}_match
			UNPACK_FUNC=${UNPACKER}_unpack
			$MATCH_FUNC $SOURCE
			if [ $? -eq 0 ]; then
				message "Using $UNPACKER module to extract $SOURCE."
				$UNPACK_FUNC $SOURCE
			fi
		done
	done
	if [ "$PPKGMK_UNPACK_ONLY" ]; then
		PPKGMK_KEEP_WORK="true"
		do_exit 0
	fi
}

do_build() {
	message "Building $PACKAGE"
	cd $SRC
	$build_func
}

make_package() {
	message "Creating package $PACKAGE."
	cd $PKG
	tar zcf "$PPKGMK_PACKAGE_DIR/$PACKAGE.pkg.tar.gz" *
}

generate_footprint() {
	tar ztvf $PPKGMK_PACKAGE_DIR/$PACKAGE.pkg.tar.gz \
		| awk '{print $1, $2, $6, $7, $8}' > $1
}

check_footprint() {
	FOOTPRINT_FILE=$PPKGMK_ROOT/$PPKGMK_FOOTPRINT_FILE
	FOOTPRINT_NEW=$TMP/footprint.new
	FOOTPRINT_DIFF=$TMP/footprint.diff
	if [ "$PPKGMK_UPDATE_FOOTPRINT" ]; then
		message "Updating footprint."
	fi
	if [ ! -f $FOOTPRINT_FILE ]; then
		PPKGMK_UPDATE_FOOTPRINT="true"
		message "Creating footprint."
	fi
	
	if [ "$PPKGMK_UPDATE_FOOTPRINT" ]; then
		generate_footprint $FOOTPRINT_FILE
	else
		if [ "$PPKGMK_IGNORE_FOOTPRINT" ]; then
			warning "Ignoring footprint."
		else
			message "Verifying footprint."
			generate_footprint $FOOTPRINT_NEW
			generate_diff $FOOTPRINT_FILE $FOOTPRINT_NEW $FOOTPRINT_DIFF
			if [ -s $FOOTPRINT_DIFF ]; then
				alert "Footprint mismatch:"
				cat $FOOTPRINT_DIFF
			fi
		fi
	fi
}
	
main() {
	load_data "$@"
	get_source
	verify_source
	unpack_source
	do_build
	make_package
	check_footprint
	cleanup
}

PPKGMK_ROOT="$PWD"
main "$@"
